﻿namespace MicroCloud.Authorization
{
    #region "功能权限检查基类"
    /// <summary>
    /// 功能权限检查基类
    /// </summary>
    public abstract class FunctionAuthorizationBase : IFunctionAuthorization
    {
        #region "属性"
        #region "获取 功能处理器"
        /// <summary>
        /// 获取 功能处理器
        /// </summary>
        protected IFunctionHandler FunctionHandler { get; }
        #endregion
        #region "获取 功能权限缓存"
        /// <summary>
        /// 获取 功能权限缓存
        /// </summary>
        protected IFunctionAuthCache FunctionAuthCache { get; }
        #endregion

        #endregion

        #region "构造函数"
        #region "初始化一个功能权限检查基类的新实例"
        /// <summary>
        /// 初始化一个功能权限检查基类 <see cref="FunctionAuthorizationBase"/> 的新实例
        /// </summary>
        /// <param name="functionHandler">功能处理器</param>
        /// <param name="functionAuthCache">功能权限缓存</param>
        protected FunctionAuthorizationBase(IFunctionHandler functionHandler, IFunctionAuthCache functionAuthCache)
        {
            FunctionHandler = functionHandler;
            FunctionAuthCache = functionAuthCache;
        }
        #endregion

        #endregion

        #region "方法"
        #region "检查指定用户是否有执行指定功能的权限"
        /// <summary>
        /// 检查指定用户是否有执行指定功能的权限
        /// </summary>
        /// <param name="function">要检查的功能</param>
        /// <param name="principal">在线用户信息</param>
        /// <returns>功能权限检查结果</returns>
        public AuthorizationResult Authorize(IFunction function, IPrincipal principal)
        {
            function = FunctionHandler.GetFunction(function.Id);

            return AuthorizeCore(function, principal);
        }
        #endregion
        #region "获取功能权限检查通过的角色"
        /// <summary>
        /// 获取功能权限检查通过的角色
        /// </summary>
        /// <param name="function">要检查的功能</param>
        /// <param name="principal">在线用户信息</param>
        /// <returns>通过的角色</returns>
        public virtual OnlineRole[] GetOkRoles(IFunction function, IPrincipal principal)
        {
            function = FunctionHandler.GetFunction(function.Id);

            if (!principal.Identity.IsAuthenticated)
            {
                return Array.Empty<OnlineRole>();
            }

            OnlineRole[] userRoles = principal.Identity.GetRoles();
            if (function.AccessType != FunctionAccessType.RoleLimit)
            {
                //不是角色限制的功能，允许用户的所有角色
                return userRoles;
            }
            string[] functionRoles = FunctionAuthCache.GetFunctionRoles(function.Id);

            //return userRoles.Intersect(functionRoles).ToArray();
            return userRoles.Where(o => functionRoles.Contains(o.Id)).ToArray();
        }
        #endregion

        #endregion

        #region "受保护的方法"
        #region "重写以实现权限检查核心验证操作"
        /// <summary>
        /// 重写以实现权限检查核心验证操作
        /// </summary>
        /// <param name="function">要验证的功能信息</param>
        /// <param name="principal">当前用户在线信息</param>
        /// <returns>功能权限验证结果</returns>
        protected virtual AuthorizationResult AuthorizeCore(IFunction function, IPrincipal principal)
        {
            if (function == null)
            {
                return new AuthorizationResult(AuthorizationStatus.NoFound);
            }
            if (function.IsLocked)
            {
                return new AuthorizationResult(AuthorizationStatus.Locked, I18N.T("功能 {0} 已禁用", function.Name));
            }
            if (function.AccessType == FunctionAccessType.Anonymous)
            {
                return AuthorizationResult.OK;
            }
            //未登录
            if (principal == null || !principal.Identity.IsAuthenticated)
            {
                return new AuthorizationResult(AuthorizationStatus.Unauthorized);
            }
            //已登录，无角色限制。
            if (function.AccessType == FunctionAccessType.LoggedIn)
            {
                return AuthorizationResult.OK;
            }
            return AuthorizeRoleLimit(function, principal);
        }
        #endregion
        #region "重写以实现 角色限制 的功能的功能权限检查"
        /// <summary>
        /// 重写以实现 角色限制 的功能的功能权限检查
        /// </summary>
        /// <param name="function">要验证的功能信息</param>
        /// <param name="principal">用户在线信息</param>
        /// <returns>功能权限验证结果</returns>
        protected virtual AuthorizationResult AuthorizeRoleLimit(IFunction function, IPrincipal principal)
        {
            //角色限制
            if (principal.Identity is not ClaimsIdentity identity)
            {
                return new AuthorizationResult(AuthorizationStatus.Error, I18N.T("当前用户标识 Identity 格式不正确，仅支持 ClaimsIdentity 类型的用户标识"));
            }
            //检查角色-功能的权限
            OnlineRole[] userRoles = identity.GetRoles().ToArray();
            AuthorizationResult result = AuthorizeRoles(function, userRoles);
            if (result.IsOk)
            {
                return result;
            }
            result = AuthorizeUser(function, principal.Identity.GetUserId());
            return result;
        }
        #endregion
        #region "重写以实现指定角色是否有执行指定功能的权限"
        /// <summary>
        /// 重写以实现指定角色是否有执行指定功能的权限
        /// </summary>
        /// <param name="function">功能信息</param>
        /// <param name="userRoles">用户角色集合</param>
        /// <returns>功能权限检查结果</returns>
        protected virtual AuthorizationResult AuthorizeRoles(IFunction function, params OnlineRole[] userRoles)
        {
            Check.NotNull(userRoles, nameof(userRoles));

            if (userRoles.Length == 0)
            {
                return new AuthorizationResult(AuthorizationStatus.Forbidden);
            }
            if (function.AccessType != FunctionAccessType.RoleLimit || userRoles.Any(o => o.IsAdmin))
            {
                return AuthorizationResult.OK;
            }
            string[] functionRoleIds = FunctionAuthCache.GetFunctionRoles(function.Id);
            if (userRoles.Any(o => functionRoleIds.Contains(o.Id)))
            {
                return AuthorizationResult.OK;
            }
            return new AuthorizationResult(AuthorizationStatus.Forbidden);
        }
        #endregion
        #region "重写以实现指定用户是否有执行指定功能的权限"
        /// <summary>
        /// 重写以实现指定用户是否有执行指定功能的权限
        /// </summary>
        /// <param name="function">功能信息</param>
        /// <param name="userId">用户编号</param>
        /// <returns>功能权限检查结果</returns>
        protected virtual AuthorizationResult AuthorizeUser(IFunction function, string userId)
        {
            if (function.AccessType != FunctionAccessType.RoleLimit)
            {
                return AuthorizationResult.OK;
            }

            var functionIds = FunctionAuthCache.GetUserFunctions(userId);
            if (functionIds.Contains(function.Id))
            {
                return AuthorizationResult.OK;
            }
            return new AuthorizationResult(AuthorizationStatus.Forbidden);
        }
        #endregion

        #endregion

    }
    #endregion

}
